/*
 * CardCopierUI.java
 *
 * Created on 14 April 2007, 21:23
 */

package org.cardcopier.ui;

import java.awt.event.ActionEvent;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.List;
import javax.swing.JOptionPane;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableColumn;
import org.apache.log4j.Logger;
import org.cardcopier.CardCopier;
import org.cardcopier.CardCopierModel;
import org.cardcopier.ConfigFile;
import org.cardcopier.file.ByteSizeFormatter;

/**
 *
 * @author  Mark
 */
public class CardCopierUI extends javax.swing.JFrame {

    private class FileDetailsTableModelListener implements TableModelListener {
        
        private NumberFormat countFormatter = new DecimalFormat("#,###");
        
        public void tableChanged(TableModelEvent e) {
            FileDetailsTableModel model = (FileDetailsTableModel) e.getSource();
            int numFiles = model.getRowCount();
            int numNewFiles = 0;
            int numExistingFiles = 0;
            for (int i = 0; i < model.getRowCount(); i++) {
                if (model.fileExistsInDestination(i)) {
                    numExistingFiles++;
                } else {
                    numNewFiles++;
                }
            }            
            numNewFilesLabel.setText(createFileCountLabel("New", numNewFiles));
            numExistingFilesLabel.setText(createFileCountLabel("Existing", numExistingFiles));
        }

        private String createFileCountLabel(String fileType, int count) {
            StringBuffer b = new StringBuffer();
            if (count == 0) {
                b.append("No ").append(fileType).append(" files");
            } else if (count == 1) {
                b.append("1 ").append(fileType).append(" file");
            } else {                
                b.append(countFormatter.format(count)).append(" ").append(fileType).append(" files");
            }
            return b.toString();
        }
    }
    
    private class FileSelectionListener implements ListSelectionListener {
        private FileDetailsTableModel tableModel = (FileDetailsTableModel) fileDetailsTable.getModel();
        private ByteSizeFormatter sizeFormatter = new ByteSizeFormatter();
        
        public void valueChanged(ListSelectionEvent e) {
            if (e.getValueIsAdjusting()) {
                return;
            }
            
            ListSelectionModel lsm = (ListSelectionModel)e.getSource();
            
            long totalSizeOfSelection = 0;
            if (lsm.isSelectionEmpty()) {
                sizeOfSelectedFiles.setText("No files selected");
            } else {
                totalSizeOfSelection = calculateSelectionSize(lsm);
                String sizeText = sizeFormatter.format(totalSizeOfSelection) + " of files selected";
                sizeOfSelectedFiles.setText(sizeText);
            }
            checkSpaceAvailable(totalSizeOfSelection);
        }
        
        private long calculateSelectionSize(ListSelectionModel lsm) {
            int minIndex = lsm.getMinSelectionIndex();
            int maxIndex = lsm.getMaxSelectionIndex();
            long totalSizeOfSelection = 0;
            for (int i = minIndex; i <= maxIndex; i++) {
                if (lsm.isSelectedIndex(i)) {
                    totalSizeOfSelection += tableModel.getFileStateAt(i).getFileLengthInBytes();
                }
            }
            return totalSizeOfSelection;
        }
        
        private void checkSpaceAvailable(long totalSizeOfSelection) {
            // TODO: MRJ 2007-06-02 If we have other parts of the UI setting messages and changing the state
            // of the copy button then this simple approach to controlling them from here will not be sufficient.
            if (totalSizeOfSelection > cardCopierModel.getSpaceAvailableInDestination()) {
                cardCopierModel.setMessageText("Not enough space available!");
                copyButton.setEnabled(false);
            } else {
                cardCopierModel.setMessageText("");
                copyButton.setEnabled(true);
            }
        }
    }
    
    private static final Logger LOGGER = Logger.getLogger(CardCopier.class);
    
    private final CardCopierModel cardCopierModel;
    private final ConfigFile configFile;
    
    private final FileDetailsTable fileDetailsTable;
    private final CopyAction copyAction;
    private final RefreshAction refreshAction;
    private final UpdatePathsAction updatePathsAction;
    private final SelectAllFilesAction selectAllFilesAction;
    private final SelectNewFilesAction selectNewFilesAction;
    private final ExitAction exitAction;
    
    
    /** Creates new form CardCopierUI */
    public CardCopierUI(CardCopierModel cardCopierModel, ConfigFile configFile) {
        super("Flash Card Copier");
        this.cardCopierModel = cardCopierModel;
        this.configFile = configFile;
        
        // Have to create the actions before initialising the visual components
        this.refreshAction = new RefreshAction(cardCopierModel);
        this.updatePathsAction = new UpdatePathsAction(cardCopierModel, this);
        this.exitAction = new ExitAction(this);
        initComponents();

        this.fileDetailsTable = (FileDetailsTable)fileDetailsJTable;
        this.selectAllFilesAction = new SelectAllFilesAction(fileDetailsTable);
        selectAllButton.setAction(selectAllFilesAction);
        this.selectNewFilesAction = new SelectNewFilesAction(fileDetailsTable);
        selectNewButton.setAction(selectNewFilesAction);
        this.copyAction = new CopyAction(cardCopierModel, fileDetailsTable, this);
        copyButton.setAction(copyAction);
        
        UICardCopierModelListener listener = new UICardCopierModelListener(destinationTextField, spaceAvailableLabel, fileDetailsTable, messageText);
        cardCopierModel.addCardCopierModelListener(listener);
        fileDetailsTable.getModel().addTableModelListener(new FileDetailsTableModelListener());
        fileDetailsTable.getSelectionModel().addListSelectionListener(new FileSelectionListener());
        //listener.sourceLocationChanged(cardCopierModel);
        fileDetailsTable.setInitialColumnWidths();
        
        if (cardCopierModel.getDeleteSourceFileAfterCopy()) {
            moveActionButton.setSelected(true);
        } else {
            copyActionButton.setSelected(true);
        }
        updateCopyButtonText(cardCopierModel.getDeleteSourceFileAfterCopy());
    }
    
    public void askForInitialPaths() {
        ActionEvent e = new ActionEvent(updatePathsAction.INITIAL_PATH_SETUP, ActionEvent.ACTION_PERFORMED, updatePathsAction.INITIAL_PATH_SETUP);
        updatePathsAction.actionPerformed(e);
    }

    private void updateCopyButtonText(boolean move) {
        copyButton.setText(move ? "Move" : "Copy");
    }
    
    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
    private void initComponents() {
        actionButtonGroup = new javax.swing.ButtonGroup();
        fileDetailsScroller = new javax.swing.JScrollPane();
        fileDetailsJTable = new FileDetailsTable();
        refreshButton = new javax.swing.JButton();
        copyButton = new javax.swing.JButton();
        updatePathsButton = new javax.swing.JButton();
        destinationLabel = new javax.swing.JLabel();
        destinationTextField = new javax.swing.JTextField();
        selectAllButton = new javax.swing.JButton();
        selectNewButton = new javax.swing.JButton();
        messageText = new javax.swing.JLabel();
        numNewFilesLabel = new javax.swing.JLabel();
        numExistingFilesLabel = new javax.swing.JLabel();
        copyActionLabel = new javax.swing.JLabel();
        copyActionButton = new javax.swing.JRadioButton();
        moveActionButton = new javax.swing.JRadioButton();
        spaceAvailableLabel = new javax.swing.JLabel();
        sizeOfSelectedFiles = new javax.swing.JLabel();
        menuBar = new javax.swing.JMenuBar();
        fileMenu = new javax.swing.JMenu();
        Preferences = new javax.swing.JMenuItem();
        ExitMenuItem = new javax.swing.JMenuItem();

        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowClosing(java.awt.event.WindowEvent evt) {
                formWindowClosing(evt);
            }
        });

        fileDetailsScroller.setBackground(new java.awt.Color(255, 255, 255));
        fileDetailsScroller.setEnabled(false);
        fileDetailsJTable.setModel(new FileDetailsTableModel());
        fileDetailsScroller.setViewportView(fileDetailsJTable);

        refreshButton.setAction(refreshAction);

        copyButton.setAction(copyAction);
        copyButton.setText("Copy");
        copyButton.setToolTipText("Copy the selected files from the source to the destination");

        updatePathsButton.setAction(updatePathsAction);
        updatePathsButton.setText("Update Paths");
        updatePathsButton.setToolTipText("Update the source and destination directories");

        destinationLabel.setText("Destination:");

        destinationTextField.setEditable(false);
        destinationTextField.setText("Destination Directory");
        destinationTextField.setToolTipText("The destination directory that the files will be copied to");

        selectAllButton.setAction(selectAllFilesAction);
        selectAllButton.setText("Select All");
        selectAllButton.setToolTipText("select all files to be copied");

        selectNewButton.setAction(selectNewFilesAction);
        selectNewButton.setText("Select New");
        selectNewButton.setToolTipText("Select only new files to be copied");

        messageText.setText("messageText");

        numNewFilesLabel.setText("No New Files");

        numExistingFilesLabel.setText("No Existing Files");

        copyActionLabel.setText("Action:");

        actionButtonGroup.add(copyActionButton);
        copyActionButton.setText("Copy");
        copyActionButton.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
        copyActionButton.setMargin(new java.awt.Insets(0, 0, 0, 0));
        copyActionButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                copyActionButtonActionPerformed(evt);
            }
        });

        actionButtonGroup.add(moveActionButton);
        moveActionButton.setText("Move");
        moveActionButton.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
        moveActionButton.setMargin(new java.awt.Insets(0, 0, 0, 0));
        moveActionButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                moveActionButtonActionPerformed(evt);
            }
        });

        spaceAvailableLabel.setText("space available");

        sizeOfSelectedFiles.setText("size of selected files");

        fileMenu.setText("File");
        Preferences.setText("Preferences...");
        Preferences.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                PreferencesActionPerformed(evt);
            }
        });

        fileMenu.add(Preferences);

        ExitMenuItem.setAction(exitAction);
        fileMenu.add(ExitMenuItem);

        menuBar.add(fileMenu);

        setJMenuBar(menuBar);

        org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING)
                    .add(org.jdesktop.layout.GroupLayout.LEADING, fileDetailsScroller, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 612, Short.MAX_VALUE)
                    .add(layout.createSequentialGroup()
                        .add(selectAllButton)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(selectNewButton)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED, 109, Short.MAX_VALUE)
                        .add(updatePathsButton)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(copyButton)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(refreshButton))
                    .add(org.jdesktop.layout.GroupLayout.LEADING, layout.createSequentialGroup()
                        .add(destinationLabel)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(destinationTextField, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 550, Short.MAX_VALUE))
                    .add(org.jdesktop.layout.GroupLayout.LEADING, layout.createSequentialGroup()
                        .add(copyActionLabel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 58, Short.MAX_VALUE)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(copyActionButton)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(moveActionButton)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED, 280, Short.MAX_VALUE)
                        .add(numNewFilesLabel, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 86, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(numExistingFilesLabel))
                    .add(org.jdesktop.layout.GroupLayout.LEADING, layout.createSequentialGroup()
                        .add(10, 10, 10)
                        .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                            .add(messageText, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 602, Short.MAX_VALUE)
                            .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING, false)
                                .add(org.jdesktop.layout.GroupLayout.LEADING, spaceAvailableLabel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                .add(org.jdesktop.layout.GroupLayout.LEADING, sizeOfSelectedFiles, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 394, Short.MAX_VALUE)))))
                .addContainerGap())
        );

        layout.linkSize(new java.awt.Component[] {copyActionLabel, destinationLabel}, org.jdesktop.layout.GroupLayout.HORIZONTAL);

        layout.linkSize(new java.awt.Component[] {copyButton, refreshButton, selectAllButton, selectNewButton, updatePathsButton}, org.jdesktop.layout.GroupLayout.HORIZONTAL);

        layout.linkSize(new java.awt.Component[] {numExistingFilesLabel, numNewFilesLabel}, org.jdesktop.layout.GroupLayout.HORIZONTAL);

        layout.setVerticalGroup(
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                    .add(destinationLabel)
                    .add(destinationTextField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                    .add(numExistingFilesLabel)
                    .add(numNewFilesLabel)
                    .add(copyActionLabel)
                    .add(copyActionButton)
                    .add(moveActionButton))
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(fileDetailsScroller, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 380, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(spaceAvailableLabel)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(sizeOfSelectedFiles)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(messageText)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                    .add(refreshButton)
                    .add(copyButton)
                    .add(updatePathsButton)
                    .add(selectAllButton)
                    .add(selectNewButton))
                .addContainerGap())
        );

        layout.linkSize(new java.awt.Component[] {copyButton, refreshButton, selectAllButton, selectNewButton, updatePathsButton}, org.jdesktop.layout.GroupLayout.VERTICAL);

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void moveActionButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_moveActionButtonActionPerformed
        boolean move = true;
        cardCopierModel.setDeleteSourceFileAfterCopy(move);
        updateCopyButtonText(move);
    }//GEN-LAST:event_moveActionButtonActionPerformed

    private void copyActionButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_copyActionButtonActionPerformed
        boolean move = false;
        cardCopierModel.setDeleteSourceFileAfterCopy(move);
        updateCopyButtonText(move);
    }//GEN-LAST:event_copyActionButtonActionPerformed

    private void formWindowClosing(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosing
        try {
            configFile.writeConfig(cardCopierModel.getApplicationConfig());
        } catch (IOException e) {
            String error = "Failed to save application config because " + e.getMessage();
            LOGGER.error(error, e);
            JOptionPane.showMessageDialog(this, error, "Error", JOptionPane.ERROR_MESSAGE);
        }
    }//GEN-LAST:event_formWindowClosing

    private void PreferencesActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_PreferencesActionPerformed
// TODO add your handling code here:
    }//GEN-LAST:event_PreferencesActionPerformed
    
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JMenuItem ExitMenuItem;
    private javax.swing.JMenuItem Preferences;
    private javax.swing.ButtonGroup actionButtonGroup;
    private javax.swing.JRadioButton copyActionButton;
    private javax.swing.JLabel copyActionLabel;
    private javax.swing.JButton copyButton;
    private javax.swing.JLabel destinationLabel;
    private javax.swing.JTextField destinationTextField;
    private javax.swing.JTable fileDetailsJTable;
    private javax.swing.JScrollPane fileDetailsScroller;
    private javax.swing.JMenu fileMenu;
    private javax.swing.JMenuBar menuBar;
    private javax.swing.JLabel messageText;
    private javax.swing.JRadioButton moveActionButton;
    private javax.swing.JLabel numExistingFilesLabel;
    private javax.swing.JLabel numNewFilesLabel;
    private javax.swing.JButton refreshButton;
    private javax.swing.JButton selectAllButton;
    private javax.swing.JButton selectNewButton;
    private javax.swing.JLabel sizeOfSelectedFiles;
    private javax.swing.JLabel spaceAvailableLabel;
    private javax.swing.JButton updatePathsButton;
    // End of variables declaration//GEN-END:variables
    
}
